
#include <QPainter>
#include <material_editor/glslsyntaxhighlighter.h>
#include <material_editor/glsltexteditor.h>
#include <stdio.h>



class LineNumberArea : public QWidget
{
public:
  LineNumberArea(GLSLTextEditor *editor)
    : QWidget(editor)
    , _editor(editor)
  {
  }

  QSize sizeHint() const
  {
    return QSize(_editor->LineNumberAreaWidth(), 0);
  }

protected:
  void paintEvent(QPaintEvent *event)
  {
    _editor->LineNumberAreaPaintEvent(event);
  }

private:
  GLSLTextEditor *_editor;
};

GLSLTextEditor::GLSLTextEditor(QWidget* parent)
  : QPlainTextEdit (parent)
{
  _highlighter = new GLSLSyntaxHighlighter(document());

  _lineNumberArea = new LineNumberArea (this);
  connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(UpdateLineNumberAreaWidth(int)));
  connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(UpdateLineNumberArea(QRect,int)));
  connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(HighlightCurrentLine()));


  setWordWrapMode(QTextOption::NoWrap);
  QTextDocument* doc = document();
#ifdef _WIN32
  QFont f ("Courier");
#else
  QFont f ("Monospace");
#endif
  f.setStyleHint(QFont::Monospace);
  f.setPixelSize(14);
  doc->setDefaultFont(f);


  connect (doc, SIGNAL(contentsChange(int,int,int)), SLOT(on_document_contentChanged (int, int, int)));

  connect(doc, SIGNAL(contentsChanged()), this, SIGNAL(ContentChanged()));
}


int GLSLTextEditor::LineNumberAreaWidth()
{
  int digits = 1;
  int max = qMax(1, blockCount());
  while (max >= 10)
    {
      max /= 10;
      ++digits;
    }

  int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits;

  return space;
}


void GLSLTextEditor::UpdateLineNumberAreaWidth(int)
{
    setViewportMargins(LineNumberAreaWidth(), 0, 0, 0);
}


void GLSLTextEditor::UpdateLineNumberArea(const QRect &rect, int dy)
{
  if (dy)
    {
      _lineNumberArea->scroll(0, dy);
    }
  else
    {
      _lineNumberArea->update(0, rect.y(), _lineNumberArea->width(), rect.height());
    }

  if (rect.contains(viewport()->rect()))
    {
      UpdateLineNumberAreaWidth(0);
    }
}



void GLSLTextEditor::resizeEvent(QResizeEvent *e)
{
  QPlainTextEdit::resizeEvent(e);

  QRect cr = contentsRect();
  _lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), LineNumberAreaWidth(), cr.height()));
}


void GLSLTextEditor::HighlightCurrentLine()
{
  QList<QTextEdit::ExtraSelection> extraSelections;

  if (!isReadOnly())
    {
      QTextEdit::ExtraSelection selection;

      QColor lineColor = QColor(240, 255, 255);

      selection.format.setBackground(lineColor);
      selection.format.setProperty(QTextFormat::FullWidthSelection, true);
      selection.cursor = textCursor();
      selection.cursor.clearSelection();
      extraSelections.append(selection);
    }

  setExtraSelections(extraSelections);
}




void GLSLTextEditor::LineNumberAreaPaintEvent(QPaintEvent *event)
{
  QPainter painter(_lineNumberArea);
  painter.fillRect(event->rect(), Qt::lightGray);


  QTextBlock block = firstVisibleBlock();
  int blockNumber = block.blockNumber();
  int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
  int bottom = top + (int) blockBoundingRect(block).height();

  while (block.isValid() && top <= event->rect().bottom())
    {
      if (block.isVisible() && bottom >= event->rect().top())
        {
          QString number = QString::number(blockNumber + 1);
          painter.setPen(Qt::white);
          painter.drawText(1, top+1, _lineNumberArea->width(), fontMetrics().height(),
                           Qt::AlignRight, number);
          painter.setPen(Qt::black);
          painter.drawText(0, top, _lineNumberArea->width(), fontMetrics().height(),
                           Qt::AlignRight, number);
        }

      block = block.next();
      top = bottom;
      bottom = top + (int) blockBoundingRect(block).height();
      ++blockNumber;
    }
}


namespace
{
unsigned count_chars (const QString& text, QString ch, int position)
{
  unsigned c = 0;
  int index = text.indexOf(ch);
	while (index >= 0 && index < position)
		{
			c++;
      index = text.indexOf(ch, index + 1);
		}

	return c;
}

}
void GLSLTextEditor::on_document_contentChanged(int position, int charsRemoved, int charsAdded)
{
	if (charsAdded == 1)
		{
			QTextDocument* doc = document ();
			QString text = doc->toPlainText();
      if (text.length () <= position)
        {
          return;
        }
			if (text.at(position) == '\n')
				{
          unsigned openBraces = count_chars(text, "{", position) - count_chars(text, "}", position);
					if (!openBraces)
						{
							return;
						}

					QString add;
					for (unsigned i=0; i<openBraces; i++)
						{
							add.append("  ");
						}
					blockSignals(true);
					insertPlainText(add);
					blockSignals(false);
				}
			else if (text.at(position) == '}')
				{
					if (text.at(position-1) == ' ' && text.at(position-2) == ' ')
						{
							QTextCursor cursor = textCursor();
							cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
							cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, 2);
							cursor.insertText("");
							cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
						}
				}
		}

	fflush(stdout);
}



void GLSLTextEditor::SetCursorToLine(int line)
{
  QString text = document()->toPlainText();
  int pos = 0;
  for (int i=1; i<line; i++)
    {
      pos = text.indexOf('\n', pos);
      if (pos < 0) return;
      pos++;
    }

  QTextCursor cursor = textCursor();
  cursor.setPosition(pos);
  setTextCursor(cursor);
}
